package com.mrx.loader;

import com.mrx.translator.ITranslator;
import org.objectweb.asm.*;

import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * ASM 替换字符串用的 {@link ClassVisitor}
 */
public class TranslateClassVisitor extends ClassVisitor {

    private static final Set<ITranslator> translators = loadTranslatorPlugins();

    /**
     * 通过 SPI 加载 {@link ITranslator}
     *
     * @return 找到的翻译插件
     */
    private static Set<ITranslator> loadTranslatorPlugins() {
        return ServiceLoader.load(ITranslator.class)
                .stream().map(ServiceLoader.Provider::get)
                .collect(Collectors.toSet());
    }

    /**
     * 当前正在处理的 className
     */
    private final String className;

    public TranslateClassVisitor(String className, int api, ClassVisitor classVisitor) {
        super(api, classVisitor);
        this.className = className;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc,
                                   String signature, Object value) {
        return super.visitField(access, name, desc, signature, getTranslated(0, value));
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor,
                                     String signature, String[] exceptions
    ) {
        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
        return new MethodVisitor(Opcodes.ASM7, mv) {
            private int line = 0;

            @Override
            public void visitLdcInsn(Object value) {
                super.visitLdcInsn(getTranslated(line, value));
            }

            @Override
            public void visitLineNumber(int line, Label start) {
                this.line = line;
                super.visitLineNumber(line, start);
            }
        };
    }

    /**
     * 获取翻译后的字符串
     *
     * @param lineNumber 源字符串所在行号
     * @param value      源字符串
     * @return 翻译后的字符串
     */
    private Object getTranslated(int lineNumber, Object value) {
        if (value instanceof CharSequence) {
            // 暂时只考虑一个插件的情况
            return translators.stream()
                    .map(it -> it.translate(className, lineNumber, String.valueOf(value)))
                    .findFirst().orElseThrow();
        }
        return value;
    }

}
